package md.sv.dbu;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.joran.spi.JoranException;
import md.sv.dbu.config.XmlParser;
import md.sv.dbu.config.jaxb.DbuType;
import md.sv.dbu.db.DBExecutor;
import md.sv.dbu.executor.DbuTypeExecutor;
import md.sv.dbu.sender.MessageSender;
import md.sv.dbu.utils.PatternReplacer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import javax.xml.bind.JAXBException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author MG
 */
public class DBUExecutor {
    public static final String CURRENT_YEAR = "CURRENT_YEAR";
    public static final String CURRENT_MONTH = "CURRENT_MONTH";
    public static final String CURRENT_DAY = "CURRENT_DAY";
    public static final String NOW = "NOW";

    private static final Logger logger = LoggerFactory.getLogger(DBUExecutor.class);

    private XmlParser xmlParser;
    private MessageSender messageSender;

    private PatternReplacer globalVariablesReplacer = new PatternReplacer("\\$\\{([\\w:]+)\\}");
    private PatternReplacer sqlVariablesReplacer = new PatternReplacer("#\\{([\\w:]+)\\}");
    private PatternReplacer sqlParamsReplacer = new PatternReplacer(":([\\w:]+)");

    private String rootPath = "/";
    private ConcurrentMap<String, DBExecutor> dbExecutors = new ConcurrentHashMap<String, DBExecutor>();
    private ConcurrentMap<String, Object> variables = new ConcurrentHashMap<String, Object>();
    private ConcurrentHashMap<String, DateFormat> dateTimeFormats = new ConcurrentHashMap<String, DateFormat>();

    /**
     * The main list of executors
     */
    private List<DbuTypeExecutor> executorList = new ArrayList<DbuTypeExecutor>();

    public DBUExecutor(XmlParser xmlParser, MessageSender messageSender) {
        this.xmlParser = xmlParser;
        this.messageSender = messageSender;
    }

    public void init() {
        Calendar calendar = Calendar.getInstance();
        variables.put(CURRENT_YEAR, String.valueOf(calendar.get(Calendar.YEAR)));
        variables.put(CURRENT_MONTH, String.valueOf(calendar.get(Calendar.MONTH) + 1));
        variables.put(CURRENT_DAY, String.valueOf(calendar.get(Calendar.DAY_OF_MONTH)));
        variables.put(NOW, calendar.getTime());
    }

    public void close() {
        closeConfigurations();
    }

    public void execute(File configFile, InputStream validateFile) throws JAXBException, SAXException, FileNotFoundException {

        logger.info("Parsing configuration file [{}]", configFile.getPath());
        DbuType dbuType = xmlParser.parseConfigFile(new FileInputStream(configFile), DbuType.class, validateFile);

        for (Object dbuElement : dbuType.getConfigurationOrUpdateTaskOrReceiveMQ()) {
            for (DbuTypeExecutor executor : executorList) {
                if (executor.isEligible(dbuElement)) {
                    executor.execute(dbuElement);
                    break;
                }
            }
        }
    }

    private void closeConfigurations() {
        // close db connections
        for (DBExecutor executor : dbExecutors.values()) {
            executor.close();
        }

    }

    public void setGlobalLogging(String loggingLevel, String loggingPath) {
        System.out.println("Setting logging level to: " + loggingLevel);
        System.out.println("Setting loggin path to: " + loggingPath);
        System.setProperty("log_path", loggingPath);
        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        ContextInitializer ci = new ContextInitializer(lc);
        lc.reset();
        try {
            ci.autoConfig();
        } catch (JoranException e) {
            System.out.println("ERROR: Could not set Logging path: " + e);
        }

        ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
        root.setLevel(Level.toLevel(loggingLevel));
    }

    public void commitExecutors() {
        for (DBExecutor executor : dbExecutors.values()) {
            executor.commit();
        }
    }

    public void rollbackExecutors() {
        for (DBExecutor executor : dbExecutors.values()) {
            executor.rollback();
        }
    }

    public String generateNewGUID() {
        return UUID.randomUUID().toString();
    }

    public DBExecutor getExecutor(DBExecutor mainExecutor, String executorId) {
        return executorId != null ? dbExecutors.get(executorId) : mainExecutor;
    }

    public void addDbuTypeExecutor(DbuTypeExecutor executor) {
        executor.registerMainExecutor(this);
        this.executorList.add(executor);
    }

    public String getRootPath() {
        return rootPath;
    }

    public void setRootPath(String rootPath) {
        this.rootPath = rootPath;
    }

    public ConcurrentHashMap<String, DateFormat> getDateTimeFormats() {
        return dateTimeFormats;
    }

    public ConcurrentMap<String, Object> getVariables() {
        return variables;
    }

    public ConcurrentMap<String, DBExecutor> getDbExecutors() {
        return dbExecutors;
    }

    public XmlParser getXmlParser() {
        return xmlParser;
    }

    public MessageSender getMessageSender() {
        return messageSender;
    }

    public void setMessageSender(MessageSender messageSender) {
        this.messageSender = messageSender;
    }

    public PatternReplacer getGlobalVariablesReplacer() {
        return globalVariablesReplacer;
    }

    public PatternReplacer getSqlVariablesReplacer() {
        return sqlVariablesReplacer;
    }

    public PatternReplacer getSqlParamsReplacer() {
        return sqlParamsReplacer;
    }
}
